/*
"JSFXGEN" - JSParser.as
Modular Integrated Development Environment for JesuSonic
Project page: http://jsfxgen.googlecode.com

Copyright 2009 Lubomir I. Ivanov

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package neolit123.jsfxgen
{    
    import flash.utils.Timer;
    import flash.events.TimerEvent;
  // =========================================================
  public class JSParser
  {
    private const ptrn0:RegExp = new RegExp("(\\#|\\||\\/|\\:|\\@|\\'|\\|\\{|\\(|\\}|\\)|\\+|\\-|\\<|\\>|\\?|\\=|\\;|\\*|\\^|\\%|\\!|\\&|\\.){1}","gi");
    private const ptrn1:RegExp = new RegExp("(\\[|\\]|0|1|2|3|4|5|6|7|8|9){1}","gi");
    private const directives:Array = new Array("init","slider","sample","block","gfx","serialize");
    private const dirlength:int = directives.length;
    private var df:Function = Main.debug.df;
    private var con:Array;
    private var tmr:Timer = new Timer(300,1);
    // =========================================================
    public function JSParser():void
    {
      // @ constructor
    }
    // =========================================================
    // process
    // =========================================================
    public function process():void
    {
      // @ show loading screen first
      Main.loading.update("Generating JS code. Please wait");         
      tmr.addEventListener(TimerEvent.TIMER, doprocess);
      tmr.start();
    }
    // =========================================================
    // generate code
    // =========================================================
    private function doprocess(e:TimerEvent=null):void
    {
      tmr.removeEventListener(TimerEvent.TIMER, doprocess);
      con = Main.connections;
      // @ temp variables
      var i:int,j:int,k:int,l:int;
      var m_ar:Array = Main.m_ar;
      var modlen:int = m_ar.length;
      var codestr:String;      
      var isubk:String;
      var isubj:String;
      var isubres:String
      var idxstr:String;
      var gerror:Boolean = false;
      var gen_ar:Array = [];
      var res_ar:Array = [];
      var modsum:String;
      var modsumar:Array;
      var isub:Array;
      var isublen:int;
      var isubindex0:int;
      var isubindex1:int;
      var lastidxs:int = 1;
      var lastidxf:int = 0;      
      var conlen:int = con.length;      
      var iptrn:RegExp;
      // ---------------------------------------------------------
      // [ go trough modules and store sections in indexes
      // ---------------------------------------------------------
      df("GEN Start ---",false);
      for (i=0;i<modlen;i++)
      {
        if(m_ar[i] != undefined)
        {
          gen_ar[i] = [];
          df("GEN parsing Module [ "+i+" ]");
          gen_ar[i] = parsemod(i,m_ar[i].moddata);          
          if (gen_ar[i][0] == "error") {
            gerror = true;
            break;
          }
        }
      }
      // ---------------------------------------------------------
      // [ update sliders & filenames relative to each module
      // ---------------------------------------------------------
      for (i=0;i<modlen;i++)
      {
        if (gen_ar[i] != undefined)
        {
          // -------------------------------
          //  [ join module in one string
          // -------------------------------
          modsum = gen_ar[i].join("|||\n");
          modsum = modsum.substring(modsum.indexOf("@"),modsum.length);
          // ------------------
          //  [ do sliders
          // ------------------
          if (gen_ar[i][2] != undefined)
          {
            isub = []; 
            isub = gen_ar[i][2];
            isublen = isub.length;
            for (k=0;k<isublen;k++)
            {
              // @ check for sliderX
              isubk = isub[k];
              isubindex0 = isubk.indexOf(":");
              idxstr = lastidxs.toString();
              isubres = "slider"+idxstr;
              iptrn = new RegExp("(\\b)("+isubk.substring(0,isubindex0)+"){1}(\\b)","gi");
              modsum = modsum.replace(iptrn,isubres);
              // @ check for sliderchange(idx)
              isubres = isubk.substring(isubk.indexOf("r")+1,isubindex0);
              iptrn = new RegExp("(\\b)(sliderchange)(\\()(\\s*){1}(\\b)","gi");
              modsum = modsum.replace(iptrn,"sliderchange(");
              iptrn = new RegExp("(\\b)(sliderchange)(\\()("+isubres+"){1}(\\b)","gi");
              modsum = modsum.replace(iptrn,"sliderchange("+idxstr);
                            // @ check for slider_automate(idx)
              isubres = isubk.substring(isubk.indexOf("r")+1,isubindex0);
              iptrn = new RegExp("(\\b)(slider_automate)(\\()(\\s*){1}(\\b)","gi");
              modsum = modsum.replace(iptrn,"slider_automate(");
              iptrn = new RegExp("(\\b)(slider_automate)(\\()("+isubres+"){1}(\\b)","gi");
              modsum = modsum.replace(iptrn,"slider_automate("+idxstr);
              // @ change slider in slider&file section
              gen_ar[i][2][k] = "slider"+idxstr+isubk.substring(isubindex0,isubk.length);
              lastidxs += 1;
            }
          }
          gen_ar[i][2] = gen_ar[i][2].join("\n");
          // ------------------
          //  [ do files
          // ------------------
          if (gen_ar[i][3] != undefined)
          {
            isub = [];
            isub = gen_ar[i][3];
            isublen = isub.length;
            for (k=0;k<isublen;k++)
            {
              // @ check file index and replace it in module code
              isubk = isub[k];
              isubindex0 = isubk.indexOf(":")+1;
              isubindex1 = isubk.indexOf(",");
              isubres = isubk.substring(isubindex0,isubindex1).split(" ").join("");
              iptrn = new RegExp("(\\b)(file_open)(\\()(\\s*){1}(\\b)","gi");
              modsum = modsum.replace(iptrn,"file_open(");
              iptrn = new RegExp("(\\b)(file_open)(\\()("+isubres+"){1}(\\b)","gi");
              modsum = modsum.replace(iptrn,"file_open("+lastidxf);
              gen_ar[i][3][k] = isubk.substring(0,isubindex0)+lastidxf+isubk.substring(isubindex1,isubk.length);
              lastidxf += 1;
            }
          }
          gen_ar[i][3] = gen_ar[i][3].join("\n");
          // -------------------------------
          //  [ restore module to array
          // -------------------------------
          modsumar = modsum.split("|||\n");
          for (k=4;k<10;k++)
          {
            gen_ar[i][k] = modsumar[k];
          }
          // -------------------------------
          //  [ do connections
          // -------------------------------
          isub = [];
          isub = gen_ar[i][0];
          isublen = isub.length;
          // @ go trought variables
          for (k=0;k<isublen;k++)
          {
            // @ reset value to blank
            isubres = "";
            // @ go trought connections
            for (j=0;j<conlen;j++)
            {
              // @ there is connection at index
              if (con[j] != undefined)
              {
                // @ matches src
                if (con[j].src.name == isub[k] && con[j].src.parentid == i)
                {
                  isubres = con[j].dest.name.substring(0,con[j].dest.name.indexOf("["));
                  isubres = "_md"+con[j].dest.parentid+"_"+isubres;
                  break;
                }
                // @ matches dest
                else if (con[j].dest.name == isub[k] && con[j].dest.parentid == i)
                {
                  isubres = con[j].src.name.substring(0,con[j].src.name.indexOf("["));
                  isubres = "_md"+con[j].src.parentid+"_"+isubres;
                  break;
                }
              }
            }
            // @ get directive for this variable
            isubk = isub[k].substring(isub[k].indexOf("[")+1,isub[k].indexOf("]"));
            isubj = "_md"+i+"_"+isub[k].substring(0,isub[k].indexOf("["));
            // @ find where to add the connection
            for (j=0;j<dirlength;j++)
            {
              if (isubk == directives[j] && isubres != "")
              {
                isubj = isubj + " = " + isubres + ";\n";
                gen_ar[i][4+j] = isubj + gen_ar[i][4+j];
                break;
              }
            }
          }
        }
      }
      // ---------------------------------------------------------
      // [ join module code
      // ---------------------------------------------------------
      if (gerror) {
        df("GEN Aborted ---",true);
      } else {      
        // @ add header
        var header:String = "";
        var dt:Date = new Date();
        header += "// author: "+Main.arranger.author+"\n";
        header += "//   date: "+dt.getDate()+"."+(dt.getMonth()+1)+"."+dt.getFullYear()+"\n";
        header += "//  using: JSfxGEN v: "+Main.version+"\n";
        header += "\ndesc: "+Main.arranger.desc+"\n";
        // @ go trough sections in separate modules
        var modintlen:int = 10;
        res_ar[0] = res_ar[1] = "";
        for (j=2;j<modintlen;j++)
        {
          res_ar[j] = "";
          // @ add directive text
          if (j>3) res_ar[j] = "@"+directives[j-4];
          // @ add gfx width, height
          if (j==8 && Main.arranger.w > 0) res_ar[j] += " "+Main.arranger.w+" "+Main.arranger.h;
          res_ar[j] += "\n";
          // @ go trough modules and add sections to new array
          for (i=0;i<modlen;i++)
          {
            // @ check if there is module at index
            if (gen_ar[i] != undefined)
            {
              // @ if index not empty
              if (gen_ar[i][j] != "" && gen_ar[i][j] != undefined && gen_ar[i][j] != "\n")
              {
                // @ if at gfx directive and module has width, height add mod x,y offset
                if (j==8 && Main.m_ar[i].modw > 0)
                {
                  res_ar[j] +="_md"+i+"_modxo = "+Main.arranger.xyar[i][0]+";\n";
                  res_ar[j] +="_md"+i+"_modyo = "+Main.arranger.xyar[i][1]+";";
                }
                // @ add rest of the section code
                res_ar[j] += gen_ar[i][j]+"\n";
              }
            }
          }
        }
        // @ remove first 2 indexes
        res_ar = res_ar.slice(2,res_ar.length);
        res_ar[1] += "\n";
        // @ make into string
        var outstr:String = res_ar.join("\n");
        // @ remove some unnecessary line brakes
        if (outstr.indexOf("\n\n\n")==0) outstr=outstr.substring(3,outstr.length);
        outstr = outstr.split("\n\n\n").join("\n\n");
        // @ open string in editor
        Main.editor.openjs(header+outstr);
        df("GEN Successful ---",false);
        // @ hide loader when done
        Main.loading.hide();
      }      
     }
     // =========================================================
     // parse individual modules
     // =========================================================
     public function parsemod(modid:int,datastr:String):Array
     {
      // @ temp variables
      var derror:Boolean = false;
      var ud:Boolean;      
      var str:String = datastr.split("\r\n").join("\n");
      str = str.split("\r").join("\n");
      var sep0:String = "//#mspr";
      var sep1:String = "\n"+sep0+"\n";
      var arstr:String;
      var vsep:String;
      var tmpvstr:String = "";
      var checkdline:String = "";
      var tmpgfxstr:String;
      var tmpw:Number;
      var tmph:Number;      
      var ar:Array = str.split(sep1);
      var tmpar:Array = [];
      var tmpchkar:Array = [];
      var tmpchkarlen:int;
      var mlen:int;
      var k:int,p:int;
      // ---------------------------------------
      // [ missing directives
      // ---------------------------------------
      for (k=0;k<dirlength;k++)
      {        
        // @ missing directive     
        if (str.indexOf("\n@"+directives[k]) == -1)
        {
          derror = true;
          df("Module [ "+modid+" ] has missing directive - " + directives[k],true);
        }        
      }      
      // -------------------------------------------------------------
      // [ if no missing directives begin with parsing each section 
      // -------------------------------------------------------------
      if (!derror)
      {
        for (var i:int=0;i<ar.length;i++)
        {              
          arstr = ar[i];
          // ---------------------------------------
          // [ modset
          // ---------------------------------------
          if (i==1)
          {
            var modset:Array = arstr.split("\n");
            // @ error level: inputs
            // ######################
            if (arstr.indexOf("//#inputs:") == -1)
            {
              df("Module [ "+modid+" ] - cannot get inputs",true);
              derror = true;
            }
            // @ no ouput section
            // ######################
            if (arstr.indexOf("//#outputs:") == -1)
            {
              df("Module [ "+modid+" ] - cannot get outputs",true);
              derror = true;
            }
            // @ no declare section 
            // ######################
            if (arstr.indexOf("//#declare:") == -1)
            {
              df("Module [ "+modid+" ] - cannot get varible declarations",true); 
              derror = true;
            }
            // -----------------------------------------------
            // [ check for syntax and add variables if ok
            // -----------------------------------------------
            checkdline = "";
            mlen = modset.length;
            var ins:Array = [];
            var outs:Array = [];
            var vars:Array = [];
            // @ get individual modset lines
            for (var j:int=0;j<mlen;j++)
            {
              checkdline = modset[j].substring(modset[j].indexOf(":")+1,modset[j].length).split(" ").join("");
              // @ illegal characters
              // ######################
              if (checkdline.search(ptrn0) != -1)
              {
                df("Module [ "+modid+" ], modset line [ "+j+" ] contains illegal characters",true);
                derror = true;
              }
              // @ store variables in array
              tmpchkar = checkdline.split(",");
              tmpchkarlen = tmpchkar.length;
              var tmpdcheck:String;
              // @ get individual variables if a modset line is valid
              if ((modset[j].indexOf("//#inputs:") == 0 || modset[j].indexOf("//#outputs:") == 0 || modset[j].indexOf("//#declare:") == 0) && checkdline.length > 0)
              {
                for (k=0;k<tmpchkarlen;k++)
                {
                  tmpdcheck = tmpchkar[k];
                  // @ bad formatting 
                  // ######################
                  if (tmpdcheck.substring(0,1).search(ptrn1) != -1)
                  {
                    df("Module [ "+modid+" ], modset line [ "+j+" ], offset [ "+k+" ] contains bad variable formattng",true);
                    derror = true;
                  }
                  // @ empty declaration
                  // ######################
                  if (tmpdcheck == "" && tmpchkarlen > 1)
                  {
                    df("Module [ "+modid+" ], modset line [ "+j+" ], offset [ "+k+" ] contains empty declaration",true);
                    derror = true;
                  }
                  // @ should contain a known directive
                  if (modset[j].indexOf("//#inputs:") != -1 || modset[j].indexOf("//#outputs:") != -1)
                  {
                    // @ directive linkage 
                    // ######################                  
                    if (tmpdcheck.substring(tmpdcheck.indexOf("[")+1,tmpdcheck.lastIndexOf("]")).length < 1 && !derror)
                    {
                      df("Module [ "+modid+" ], modset line [ "+j+" ], offset [ "+k+" ] I/O variable requires linkage to directive",true);
                      derror = true;
                    }
                     // @ unknown directive
                     // ######################
                    ud = true;
                    // @ last symbol should be "]"
                    if (tmpdcheck.lastIndexOf("]") == tmpdcheck.length - 1)
                    {
                      for (p=0;p<dirlength;p++)
                      {
                        // @ should contain a know directive
                        if (tmpdcheck.substring(tmpdcheck.indexOf("[")+1,tmpdcheck.lastIndexOf("]")) == directives[p])
                        {
                          ud = false;
                          break;
                        }
                      }
                    }
                    if (ud && !derror)
                    {
                      df("Module [ "+modid+" ], modset line [ "+modset[j].substring(0,modset[j].indexOf(":"))+" ], offset [ "+k+" ] linkage to unknown directive",true);
                      derror = true;
                    }
                  }
                  // -------------------------------------------------
                  // @ store vars in array if no error and not empty
                  // -------------------------------------------------
                  if (!derror && tmpdcheck != "")
                  {
                    // @ add to ins or outs
                    if (modset[j].indexOf("//#inputs:") == 0) ins.push(tmpdcheck);
                    if (modset[j].indexOf("//#outputs:") == 0) outs.push(tmpdcheck);
                    // @ clear right-brackets and add to main variable list
                    if (tmpdcheck.indexOf("[") != -1) tmpdcheck = tmpdcheck.substr(0,tmpdcheck.indexOf("["));
                    vars.push(tmpdcheck);
                  }
                }
              }
            }
            // --------------------------------------------
            // [ store inputs, outputs and declarations 
            // --------------------------------------------
            tmpar[0] = [];
            tmpar[0] = ins;
            tmpar[1] = [];
            tmpar[1] = outs;
          }
          // ---------------------------------------
          // [ sliders and files
          // ---------------------------------------
          if (i==2)
          {
             var tmpsec:Array = arstr.split("\n");
             var sl:Array = [];
             var fl:Array = [];
             var tmpseclen:int = tmpsec.length;
             for (k=0;k<tmpseclen;k++)
             {
              if (tmpsec[k].indexOf("slider") == 0)
              {
                sl.push(tmpsec[k]);    
              }
              if (tmpsec[k].indexOf("filename:") == 0)
              {
                fl.push(tmpsec[k]);
              }
            }
            // @ write sliders in second index
            tmpar[2] = [];
            tmpar[2] = sl;
            // @ write filenames in third index
            tmpar[3] = [];
            tmpar[3] = fl;
          }
          // ---------------------------------------
          // [ directives
          // ---------------------------------------
          if (arstr.charAt(0) == "@")
          {
            for (k=0;k<vars.length;k++)
            {
              if (vars[k] != "")
              {
                // @ make all declared variables unique for this module
                var ptrn:RegExp = new RegExp("(?=(?:(?:([^']*)'(?1)'(?1))+|(?1))$)("+vars[k]+")(\\b)","gi");
                arstr = arstr.replace(ptrn,"_md"+modid+"_"+vars[k]);
              }
            }
            for (k=0;k<dirlength;k++)
            {
              if (arstr.indexOf("@"+directives[k]) == 0)
              {
                // @ has gfx w,h
                if (arstr.indexOf("@gfx") == 0)
                {
                  // @ set tmp gfxstr
                  tmpgfxstr = arstr.substring(arstr.indexOf("x")+2,arstr.indexOf("\n"));
                  tmpgfxstr = tmpgfxstr.split("  ").join(" ");
                  tmpgfxstr = tmpgfxstr.split(" \n").join("");
                  if (tmpgfxstr.length > 1)
                  {
                    tmpw = Number(tmpgfxstr.substring(0,tmpgfxstr.indexOf(" ")));
                    tmph = Number(tmpgfxstr.substring(tmpgfxstr.indexOf(" "),tmpgfxstr.length)); 
                    if (tmpw.toString() != "NaN" && tmph.toString() != "NaN" && tmpw != 0 && tmph != 0)
                    {
                      Main.m_ar[modid].modw = tmpw;
                      Main.m_ar[modid].modh = tmph;
                    } else {
                      df("Module [ "+modid+" ], @gfx width/height is incorrectly set",true);
                      derror = true;
                    }
                  }
                  // @ no has gfx w,h
                  else
                  {
                    Main.m_ar[modid].modw = Main.m_ar[modid].modh = 0;
                  }
                }
                // @ write the sections in rest of the indexes
                tmpar[k+4] = arstr.substring(arstr.indexOf("\n"),arstr.length);
                break;
              }
            }
            // @ strip before first line break
            arstr = arstr.substring(arstr.indexOf("\n")+1,arstr.length);
          }
        }
      }
      // ---------------------------------------
      // [ return data or error 
      // ---------------------------------------
      if (!derror) ar = tmpar else ar[0] = "error";
      return ar;
    }
  } 
}